#pragma once
#include <iostream>
#include <cstdio>
#include <string>
#include <filesystem>
#include <sstream>
#include <fstream>
#include <memory>
#include <ctime>
#include <unistd.h>
#include <time.h>
#include "mutex.hpp"

namespace LogModule
{
    using namespace MutexModule;
    /* 1. 实现策略模式 */
    const std::string gsep = "\r\n";
    /* 基类 LogStrategy */
    class LogStrategy
    {
    public:
        virtual ~LogStrategy() = default; /* 将基类的析构函数设置为虚函数，防止内存泄漏 */
        virtual void SyncLog(const std::string &message) = 0; /* 纯虚函数 */
    };

    /* 子类 ConsoleLogStrategy */
    class ConsoleLogStrategy : public LogStrategy
    {
    public:
        ConsoleLogStrategy() {}
        /* 重写函数SyncLog */
        virtual void SyncLog(const std::string &message) override
        {
            {
                /* RAII风格的锁 */
                LockGuard lockguard(_mutex);
                std::cout << message << gsep;
            }
        }
        ~ConsoleLogStrategy() {}

    private:
        Mutex _mutex; /* 锁 --> 保证打印信息的原子性 */
    };

    /* 子类 FileLogStrategy */
    const std::string defaultpath = "./log";
    const std::string defaultfilename = "my.log";
    class FileLogStrategy : public LogStrategy
    {
    public:
        FileLogStrategy(const std::string &path = defaultpath, const std::string& filename = defaultfilename)
            : _path(path),
              _file_name(filename)
        {
                if(std::filesystem::exists(_path)) return;
                try
                {
                    std::filesystem::create_directories(_path);
                }
                catch(const std::filesystem::filesystem_error &e)
                {
                    std::cerr << e.what() << '\n';
                }
        }

        virtual void SyncLog(const std::string &message) override
        {
            {
                /* RAII风格的锁 */
                LockGuard lockguard(_mutex);
                std::string filename = _path + (_path.back() == '/' ? "" : "/") + _file_name;
                std::ofstream out(filename, std::ios::app);
                if(!out.is_open()) return;
                out << message << gsep;
                out.close();
            }
        }

        ~FileLogStrategy() {}

    private:
        std::string _path;      /* 路径名 */
        std::string _file_name; /* 文件名 */
        Mutex _mutex; /* 锁 --> 保证文件访问的原子性 */
    };

    /* 日志等级 */
    enum class LogLevel
    {
        DEBUG = 1,
        INFO,
        WARING,
        ERROR,
        FATAL
    }; 

    /* 将对应的日志等级转换为string */
    std::string LevelToStr(LogLevel level)
    {
        switch (level)
        {
        case LogLevel::DEBUG:
            return "DEBUG";
        case LogLevel::INFO:
            return "INFO";
        case LogLevel::WARING:
            return "WARING";
        case LogLevel::ERROR:
            return "ERROR";
        case LogLevel::FATAL:
            return "FATAL";
        default:
            return "UNKONW";
        }
    }

    /* 获取当前时间 */
    std::string GetTimeStemp()
    {
        time_t curr = ::time(nullptr);
        struct tm curtime;
        localtime_r(&curr, &curtime);
        char timebuffer[1024];
        snprintf(timebuffer, sizeof(timebuffer), "%4d-%02d-%02d %02d:%02d:%02d", 
            curtime.tm_year + 1900,
            curtime.tm_mon + 1,
            curtime.tm_mday,
            curtime.tm_hour,
            curtime.tm_min,
            curtime.tm_sec
        );
        return timebuffer;
    }

    class Logger
    {
    public:
        Logger()
        {
            /* 默认打印到屏幕 */
            EnableConsole();
        }

        void EnableFile()
        {
            /* 文件打印模式 */
            _strategy = std::make_shared<FileLogStrategy>();
        }

        void EnableConsole()
        {
            /* 屏幕打印模式 */
            _strategy = std::make_shared<ConsoleLogStrategy>();
        }
    
        // 一条完整的信息: [2025-04-10 12:27:03] [DEBUG] [202938] [main.cc] [16] + 消息内容
        /* 内部类 */
        class LogMessages
        {
        public:
            LogMessages(LogLevel &logLevel, std::string &src_name, int line_number, Logger &logger)
                :_currtime(GetTimeStemp()),
                _loglevel(logLevel),
                _curpid(getpid()),
                _filename(src_name),
                _line(line_number),
                _logger(logger)
            {
                std::stringstream ss;
                ss << "[" << _currtime << "] "
                   << "[" << LevelToStr(_loglevel) << "] "
                   << "[" << _curpid << "] "
                   << "[" << _filename << "] "
                   << "[" << _line << "] "
                   << " - ";
                _loginfo = ss.str();
            }

            // LogMessages() << "hell world" << "XXXX" << 3.14 << 1234
            template <typename T>
            LogMessages &operator<<(const T &info)
            {
                // a = b = c =d;
                // 日志的右半部分,可变的
                std::stringstream ss;
                ss << info;
                _loginfo += ss.str();
                return *this;
            }

            ~LogMessages()
            {
                if(_logger._strategy)
                {
                    _logger._strategy->SyncLog(_loginfo);
                }
            }

        private:
            std::string _currtime; /* 当前时间 */
            LogLevel _loglevel; /* 日志等级 */
            pid_t _curpid;  /* 当前执行流pid */
            std::string _filename; /* 打印对应日志的文件名 */
            int _line;  /* 行号 */
            std::string _loginfo; /* 总体的日志信息 */
            Logger &_logger;
        };

        LogMessages operator()(LogLevel level, std::string name, int line)
        {
            return LogMessages(level, name, line, *this);
        }

        ~Logger()
        {

        }

    private:
        std::shared_ptr<LogStrategy> _strategy; /* 指向策略模式基类的指针 */
    };

    // 全局日志对象
    Logger log;
    
    // 使用宏，简化用户操作，获取文件名和行号
    #define LOG(level) log(level, __FILE__, __LINE__)
    #define Enable_Console_Log_Strategy() log.EnableConsole();
    #define Enable_File_Log_Strategy() log.EnableFile();

}
